//------------------------------------------------------------------------------
// <copyright file="RpcHooks.cpp" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//------------------------------------------------------------------------------

#include "stdafx.h"

#include "RpcHooks.h"
#include "RpcHookCode.h"
#include "RpcErrorCodes.h"
#include <stdio.h>
#include <string>
#include <algorithm>
#include <memory>
#include "UserDefinedTypeHooks.h"
#include "TestHooksErrorCodes.h"

#define TEST_EXCEPTION_1 MAKE_HOOK_ERROR(600)
#define TEST_EXCEPTION_2 MAKE_HOOK_ERROR(601)
#define TEST_EXCEPTION_3 MAKE_HOOK_ERROR(602)

namespace
{	
	Microsoft::Internal::GamesTest::Rpc::Server::RpcServer* pRpcServer;
	Microsoft::Internal::GamesTest::Rpc::Server::RpcServer* pRpcServer2;
	Microsoft::Internal::GamesTest::Rpc::Server::RpcServer* pRpcServer3;
	Microsoft::Internal::GamesTest::Rpc::Server::RpcServer* pRpcServerWithQueueSize;

	__int16 testHookOrderCounter = 0;
}

using namespace Microsoft::Internal::GamesTest::Rpc::Server;

namespace Microsoft { namespace Internal { namespace GamesTest { namespace Rpc { namespace Server { namespace TestHooks
{
	// Initialize the Direct3D resources required to run.
	void Initialize(int port)
	{
		pRpcServer = new Microsoft::Internal::GamesTest::Rpc::Server::RpcServer(port == 0 ? RpcServer::GetDefaultPort() : port);

		pRpcServer->RegisterHookMethod(L"OutputNoInput", OutputNoInput);
		pRpcServer->RegisterHookMethod(L"InputNoOutput", InputNoOutput);
		pRpcServer->RegisterHookMethod(L"InputOutput", InputOutput);
		pRpcServer->RegisterHookMethod(L"NoInputNoOutput", NoInputNoOutput);
		pRpcServer->RegisterHookMethod(L"MultiInputMultiOutput", MultiInputMultiOutput);
		pRpcServer->RegisterHookMethod(L"HookTimesOut", HookTimesOut);
		pRpcServer->RegisterHookMethod(L"HookWaitTenSeconds", HookWaitTenSeconds);
		pRpcServer->RegisterHookMethod(L"ReturnLargeNumberOfValues", ReturnLargeNumberOfValues);
		pRpcServer->RegisterHookMethod(L"ReturnErrorCode", ReturnErrorCode);
		pRpcServer->RegisterHookMethod(L"SimultaneousHookCalls", SimultaneousHookCalls);
		pRpcServer->RegisterHookMethod(L"LargeNumberOfInputValues", LargeNumberOfInputValues);
		pRpcServer->RegisterHookMethod(L"EchoStrings", EchoStrings);
		pRpcServer->RegisterHookMethod(L"RetrieveMissingRpcArchiveField", RetrieveMissingRpcArchiveField);
		pRpcServer->RegisterHookMethod(L"StringBufferIsTooSmall", StringBufferIsTooSmall);
		pRpcServer->RegisterHookMethod(L"RpcArchiveValueIsNull", RpcArchiveValueIsNull);
		pRpcServer->RegisterHookMethod(L"CallProcessPendingRequestsOnceOnServer3", CallProcessPendingRequestsOnceOnServer3);
		pRpcServer->RegisterHookMethod(L"InputOutputIntCollection", InputOutputIntCollection);
		pRpcServer->RegisterHookMethod(L"InputOutputInt64Collection", InputOutputInt64Collection);
		pRpcServer->RegisterHookMethod(L"InputOutputFloatCollection", InputOutputFloatCollection);
		pRpcServer->RegisterHookMethod(L"InputOutputStringCollection", InputOutputStringCollection);
		pRpcServer->RegisterHookMethod(L"InputOutputIntCollectionAndIntNumber", InputOutputIntCollectionAndIntNumber);
		pRpcServer->RegisterHookMethod(L"InputOutputMultipleCollections", InputOutputMultipleCollections);
		pRpcServer->RegisterHookMethod(L"TriggerEvent", TriggerEvent);
		pRpcServer->RegisterHookMethod(L"AddArchiveToArchive", AddArchiveToArchive);
		pRpcServer->RegisterHookMethod(L"AddArchiveToArchiveWithIntColl", AddArchiveToArchiveWithIntColl);
		pRpcServer->RegisterHookMethod(L"AddArchiveToArchiveWithStringColl", AddArchiveToArchiveWithStringColl);
		pRpcServer->RegisterHookMethod(L"AddArchiveToArchiveWithInt64Coll", AddArchiveToArchiveWithInt64Coll);
		pRpcServer->RegisterHookMethod(L"InputOutputArchiveCollectionToArchive", InputOutputArchiveCollectionToArchive);
		pRpcServer->RegisterHookMethod(L"InputOutputArchiveCollectionToArchiveCollections", InputOutputArchiveCollectionToArchiveCollections);
		pRpcServer->RegisterHookMethod(L"InputRpcArchiveToAnotherRpcArchiveForLoad", InputRpcArchiveToAnotherRpcArchiveForLoad);
		pRpcServer->RegisterHookMethod(L"InputRpcArchiveToAnotherRpcArchiveCollectionForLoad", InputRpcArchiveToAnotherRpcArchiveCollectionForLoad);
		pRpcServer->RegisterHookMethod(L"InputPlayerNoOutput", InputPlayerNoOutput);
		pRpcServer->RegisterHookMethod(L"NoInputPlayerOutput", NoInputPlayerOutput);
		pRpcServer->RegisterHookMethod(L"InputPlayerOutputPlayer", InputPlayerOutputPlayer);
		pRpcServer->RegisterHookMethod(L"ThrowException", ThrowException);
		pRpcServer->RegisterHookMethod(L"AddBinaryDataToArchive", AddBinaryDataToArchive);
		pRpcServer->RegisterHookMethod(L"AddBinaryDataCollectionToArchive", AddBinaryDataCollectionToArchive);
		pRpcServer->RegisterHookMethod(L"NoInputBinaryDataOutput", NoInputBinaryDataOutput);
		pRpcServer->RegisterHookMethod(L"EchoBinary", EchoBinary);
		pRpcServer->RegisterHookMethod(L"StartServerWithQueueSize", StartServerWithQueueSize);
		pRpcServer->RegisterHookMethod(L"StopServerWithQueueSize", StopServerWithQueueSize);
		pRpcServer->RegisterHookMethod(L"RunServerWithQueueSize", RunServerWithQueueSize);

		HRESULT hr = pRpcServer->Start();
		if(FAILED(hr))
		{
			OutputDebugString(L"Failed to start the first server.\n");
		}

		// Warning: If you modify this guid then you need to modify the same guid value in Rpc.Tests.XboxEndToEndTests.cs file.
		// {4FC5C96E-2ECD-4A45-BF88-59EFFC184682}
		static const GUID guid = { 0x4fc5c96e, 0x2ecd, 0x4a45, { 0xbf, 0x88, 0x59, 0xef, 0xfc, 0x18, 0x46, 0x82 } };

		pRpcServer2 = new Microsoft::Internal::GamesTest::Rpc::Server::RpcServer(guid);
		pRpcServer2->RegisterHookMethod(L"InputOutputSubtraction", InputOutputSubtraction);
		pRpcServer2->RegisterHookMethod(L"StopTheServer", StopTheServer);

		// Processing requests on server3 using server2 is utilized in TestConnectingToTwoServersWithGuidsWithoutSubscribing.
		pRpcServer2->RegisterHookMethod(L"CallProcessPendingRequestsOnceOnServer3", CallProcessPendingRequestsOnceOnServer3);

		hr = pRpcServer2->Start();

		if(FAILED(hr))
		{
			OutputDebugString(L"Failed to start the second server.\n");
		}

		// {48810C25-BC83-4934-9085-8B00275FD4F5}
		static const GUID guid3 = { 0x48810c25, 0xbc83, 0x4934, { 0x90, 0x85, 0x8b, 0x0, 0x27, 0x5f, 0xd4, 0xf5 } };
		pRpcServer3 = new Microsoft::Internal::GamesTest::Rpc::Server::RpcServer(guid3);
		pRpcServer3->RegisterHookMethod(L"InputOutput", InputOutput);
		pRpcServer3->RegisterHookMethod(L"InputOutputImmediate", InputOutput, true);
		pRpcServer3->RegisterHookMethod(L"InputNoOutput", InputNoOutput);
		pRpcServer3->RegisterHookMethod(L"OutputNoInput", OutputNoInput);
		pRpcServer3->RegisterHookMethod(L"MultiInputMultiOutput", MultiInputMultiOutput);
		pRpcServer3->RegisterHookMethod(L"HookOrderIncrementNoOutput", HookOrderIncrementNoOutput);
		pRpcServer3->RegisterHookMethod(L"GetHookOrderCounter", GetHookOrderCounter);
		pRpcServer3->RegisterHookMethod(L"TriggerEvent", TriggerEvent);
		hr = pRpcServer3->Start();

		if(FAILED(hr))
		{
			OutputDebugString(L"Failed to start the third server.\n");
		}
	}

	HRESULT ProcessPendingRequests()
	{
		HRESULT hr1 = pRpcServer->ProcessPendingRequests();

		HRESULT hr2 = pRpcServer2->ProcessPendingRequests();

		if(FAILED(hr1))
		{
			return hr1;
		}
		else
		{
			return hr2;
		}
	}

	// Hook method to call raise event for test runs
	RpcHookCode TriggerEvent(RpcArchive& eventName, RpcArchive& eventParameter)
	{
		HRESULT hr = E_FAIL;
		
		const int StaticBufferSize = 128;
		wchar_t buf[StaticBufferSize];
		wchar_t* bufPtr = buf;
		DWORD stringLength = 0;

		CHK(eventName.MeasureNamedStringValue(L"eventName", &stringLength));
		   
		if(stringLength > StaticBufferSize)
		{
			bufPtr = new wchar_t[stringLength];				
		}		
				
		CHK(eventName.GetNamedStringValue(L"eventName", bufPtr, stringLength, &stringLength));
                                         
        CHK(eventParameter.SetNamedStringValue(L"result", L"Test Event Parameter"));

		// This need to be implemented
		CHK(pRpcServer->RaiseEvent(bufPtr,eventParameter));	

		// raise event for server for test case 12
		// If required we can optionally send server name as parameter in RpcArchive object to avoid both raise events to be called
		CHK(pRpcServer3->RaiseEvent(bufPtr,eventParameter));
		
end:
		if(bufPtr != buf)
		{
			delete[] bufPtr;
		}
		return hr;
	}	

	RpcHookCode OutputNoInput(RpcArchive& input, RpcArchive& output)
	{
		UNREFERENCED_PARAMETER(input);

		HRESULT hr = E_FAIL;

		CHK(output.SetNamedValue(L"result", 3871568));

end:
		return hr;
	}

	RpcHookCode EchoStrings(RpcArchive& input, RpcArchive& output)
	{
		HRESULT hr;
		unsigned __int16 numStrings;
		std::wstring* stringArray = nullptr;

		CHK(input.GetNamedValue(L"numStrings", &numStrings));

		stringArray = new std::wstring[numStrings];


		for(int i = 0; i < numStrings; ++i)
		{
			const int StaticBufferSize = 24;
			wchar_t buf[StaticBufferSize];
			wchar_t* bufPtr = buf;

			wchar_t fieldName[128];
			swprintf_s(fieldName, L"input%03d", i);

			DWORD stringLength = 0;
			DWORD stringBufferLength = 0;
			CHK(input.MeasureNamedStringValue(fieldName, &stringLength));

			if(stringLength > StaticBufferSize)
			{
				bufPtr = new wchar_t[stringLength];
				stringBufferLength = stringLength;
			}
			else
			{
				stringBufferLength = StaticBufferSize;
			}

			DWORD numChars;
			HRESULT hr = input.GetNamedStringValue(fieldName, bufPtr, stringBufferLength, &numChars);
			if(FAILED(hr))
			{
				if(bufPtr != buf)
				{
					delete[] bufPtr;
				}
				goto end;
			}
			else
			{
				stringArray[i] = std::wstring(bufPtr, numChars);

				if(bufPtr != buf)
				{
					delete[] bufPtr;
				}
			}
		}

		CHK(output.SetNamedValue(L"numStrings", numStrings));
		for(int i = 0; i < numStrings; ++i)
		{
			wchar_t fieldName[128];
			swprintf_s(fieldName, L"output%03d", i);

			std::wstring& outgoingString = stringArray[i];
			outgoingString = L"Echo:" + outgoingString;
			CHK(output.SetNamedStringValue(fieldName, outgoingString.c_str()));
		}

end:
		delete[] stringArray;

		return hr;
	}

	RpcHookCode InputNoOutput(RpcArchive& input, RpcArchive& output)
	{
		UNREFERENCED_PARAMETER(output);

		HRESULT hr = E_FAIL;

		float inputParam1;
		CHK(input.GetNamedValue(L"param01", &inputParam1));

		if(fabs(inputParam1 - 3.14156f) > 0.0001f)
		{
			hr = E_INVALIDARG;
			goto end;
		}
end:
		return hr;
	}

	RpcHookCode InputOutput(RpcArchive& input, RpcArchive& output)
	{
		HRESULT hr = E_FAIL;

		__int32 inputParam1 = 0;
		__int32 inputParam2 = 0;

		CHK(input.GetNamedValue(L"param01", &inputParam1));
		CHK(input.GetNamedValue(L"param02", &inputParam2));

		if(inputParam1 != 211 || inputParam2 != 87)
		{
			hr = E_INVALIDARG;
			goto end;
		}

		CHK(output.SetNamedValue(L"result", inputParam1 + inputParam2));
end:
		return hr;
	}

	RpcHookCode InputOutputIntCollection(RpcArchive& input, RpcArchive& output)
	{
		HRESULT hr = E_FAIL;

		DWORD bufferLength = 0;

		CHK(input.MeasureNamedCollection(L"param01", &bufferLength));

		if (bufferLength > 0)
		{
			std::unique_ptr<__int32[]> inputParam01(new (std::nothrow) __int32[bufferLength]);

			if (inputParam01 != nullptr)
		{
				CHK(input.GetNamedCollection(L"param01", inputParam01.get(), bufferLength));
		
				std::reverse(inputParam01.get(), inputParam01.get() + bufferLength);

				CHK(output.SetNamedCollection(L"result", inputParam01.get(), bufferLength ));
		}
		else
		{
			hr = E_OUTOFMEMORY;
		}
		}
		else
		{
			hr = E_FAIL;
		}

end:
		
		return hr;
	}

	RpcHookCode InputOutputInt64Collection(RpcArchive& input, RpcArchive& output)
	{
		HRESULT hr = E_FAIL;

		DWORD bufferLength = 0;

		CHK(input.MeasureNamedCollection(L"param01", &bufferLength));

		if (bufferLength > 0)
		{
			std::unique_ptr<__int64[]> inputParam01(new (std::nothrow) __int64[bufferLength]);

			if(inputParam01 != nullptr)
		{
				CHK(input.GetNamedCollection(L"param01", inputParam01.get(), bufferLength));
		
				std::reverse(inputParam01.get(), inputParam01.get() + bufferLength);

				CHK(output.SetNamedCollection(L"result", inputParam01.get(), bufferLength ));
		}
		else
		{
			hr = E_OUTOFMEMORY;
		}
		}
		else
		{
			hr = E_FAIL;
		}

end:
		return hr;
	}

	RpcHookCode InputOutputFloatCollection(RpcArchive& input, RpcArchive& output)
	{
		HRESULT hr = E_FAIL;

		DWORD bufferLength = 0;

		CHK(input.MeasureNamedCollection(L"param01", &bufferLength));

		if (bufferLength > 0)
		{
			std::unique_ptr<float32[]> inputParam01(new (std::nothrow) float32[bufferLength]);

			if(inputParam01 != nullptr)
		{
				CHK(input.GetNamedCollection(L"param01", inputParam01.get(), bufferLength));
		
				std::reverse(inputParam01.get(), inputParam01.get() + bufferLength);

				CHK(output.SetNamedCollection(L"result", inputParam01.get(), bufferLength ));
		}
		else
		{
			hr = E_OUTOFMEMORY;
		}
		}
		else
		{
			hr = E_FAIL;
		}

end:
		return hr;
	}

	RpcHookCode InputOutputStringCollection(RpcArchive& input, RpcArchive& output)
	{
		HRESULT hr = E_FAIL;

		DWORD numStrings = 0; 
		DWORD numStringCharacters = 0;

		CHK(input.MeasureNamedStringCollection(L"stringCollection", &numStrings, &numStringCharacters));

		if (numStrings > 0)
		{
			std::unique_ptr<wchar_t*[]> stringBuffer2(new (std::nothrow) wchar_t*[numStrings]);
			std::unique_ptr<wchar_t[]> stringContents(new (std::nothrow) wchar_t[numStringCharacters]);

		if(stringBuffer2 && stringContents)
		{
				input.GetNamedStringCollection(L"stringCollection", stringBuffer2.get(), numStrings, stringContents.get(), numStringCharacters);

				std::reverse(stringBuffer2.get(), stringBuffer2.get() + numStrings);

				CHK(output.SetNamedStringCollection(L"result", stringBuffer2.get(), numStrings));
		}
		else
		{
			hr = E_OUTOFMEMORY;
		}
		}
		else
		{
			hr = E_FAIL;
		}

end:
		return hr;
	}

	RpcHookCode InputOutputIntCollectionAndIntNumber(RpcArchive& input, RpcArchive& output)
	{
		HRESULT hr = E_FAIL;

		DWORD bufferLength = 0;

		__int32 inputParam2 = 0;

		CHK(input.GetNamedValue(L"param02", &inputParam2));

		CHK(input.MeasureNamedCollection(L"param01", &bufferLength));

		if (bufferLength > 0)
		{
			std::unique_ptr<__int32[]> inputParam01(new (std::nothrow) __int32[bufferLength]);

			if(inputParam01 != nullptr)
		{
				CHK(input.GetNamedCollection(L"param01", inputParam01.get(), bufferLength));
		
				for(DWORD i = 0; i < bufferLength; i++)
			{
				inputParam01[i] += inputParam2;
			}

				CHK(output.SetNamedCollection(L"result", inputParam01.get(), bufferLength ));
		}
		else
		{
			hr = E_OUTOFMEMORY;
		}
		}
		else
		{
			hr = E_FAIL;
		}
end:
		return hr;
	}

	RpcHookCode InputOutputMultipleCollections(RpcArchive& input, RpcArchive& output)
	{
		HRESULT hr = E_FAIL;

		// Part 1 - which is a string collection

		DWORD numStrings, numStringCharacters;

		wchar_t** stringBuffer1 = NULL;
        wchar_t* stringContents1 = NULL;
		__int32* inputParam02 = NULL;
	    wchar_t** stringBuffer3 = NULL;
        wchar_t* stringContents3 = NULL;
		float32* inputParam04 = NULL;

		CHK(input.MeasureNamedStringCollection(L"param01", &numStrings, &numStringCharacters));

		stringBuffer1 = new (std::nothrow) wchar_t*[numStrings];
		stringContents1 = new (std::nothrow) wchar_t[numStringCharacters];

		if(stringBuffer1 && stringContents1)
		{
			input.GetNamedStringCollection(L"param01", stringBuffer1, numStrings, stringContents1, numStringCharacters);

			std::reverse(stringBuffer1, stringBuffer1 + numStrings);

			CHK(output.SetNamedStringCollection(L"result1", stringBuffer1, numStrings));
		}
		else
		{
			hr = E_OUTOFMEMORY;
			goto end;
		}

		// Part 2 - which is an int collection

		DWORD bufferLength;

		CHK(input.MeasureNamedCollection(L"param02", &bufferLength));
		inputParam02 = new (std::nothrow) __int32[bufferLength];

		if(inputParam02)
		{
			CHK(input.GetNamedCollection(L"param02", inputParam02, bufferLength));
		
			std::reverse(inputParam02, inputParam02 + bufferLength);

			CHK(output.SetNamedCollection(L"result2", inputParam02, bufferLength ));
		}
		else
		{
			hr = E_OUTOFMEMORY;
			goto end;
		}

		// Part 3 - which is a string collection

		CHK(input.MeasureNamedStringCollection(L"param03", &numStrings, &numStringCharacters));

		stringBuffer3 = new (std::nothrow) wchar_t*[numStrings];
		stringContents3 = new (std::nothrow) wchar_t[numStringCharacters];

		if(stringBuffer3 && stringContents3)
		{
			input.GetNamedStringCollection(L"param03", stringBuffer3, numStrings, stringContents3, numStringCharacters);

			std::reverse(stringBuffer3, stringBuffer3 + numStrings);

			CHK(output.SetNamedStringCollection(L"result3", stringBuffer3, numStrings));
		}
		else
		{
			hr = E_OUTOFMEMORY;
			goto end;
		}

		// Part 4 - which is a float collection

		CHK(input.MeasureNamedCollection(L"param04", &bufferLength));

		inputParam04 = new (std::nothrow) float32[bufferLength];

		if(inputParam04)
		{
			CHK(input.GetNamedCollection(L"param04", inputParam04, bufferLength));
		
			std::reverse(inputParam04, inputParam04 + bufferLength);

			CHK(output.SetNamedCollection(L"result4", inputParam04, bufferLength));
		}
		else
		{
			hr = E_OUTOFMEMORY;
			goto end;
		}

end:
		if(stringBuffer1 != NULL)
		{
			delete [] stringBuffer1;
		}

		if(stringContents1 != NULL)
		{
			delete [] stringContents1;
		}

		if(inputParam02 != NULL)
		{
			delete [] inputParam02;
		}

		if(stringBuffer3 != NULL)
		{
			delete [] stringBuffer3;
		}

		if(stringContents3 != NULL)
		{
			delete [] stringContents3;
		}

		if(inputParam04 != NULL)
		{
			delete [] inputParam04;
		}

		return hr;
	}

	RpcHookCode InputOutputSubtraction(RpcArchive& input, RpcArchive& output)
	{
		HRESULT hr = E_FAIL;

		__int16 inputParam1, inputParam2;
		CHK(input.GetNamedValue(L"param01", &inputParam1));
		CHK(input.GetNamedValue(L"param02", &inputParam2));

		if(inputParam1 != 50 || inputParam2 != 30)
		{
			hr = E_INVALIDARG;
			goto end;
		}

		CHK(output.SetNamedValue(L"result", inputParam1 - inputParam2));
end:
		return hr;
	}

	RpcHookCode NoInputNoOutput(RpcArchive& input, RpcArchive& output)
	{
		UNREFERENCED_PARAMETER(input);
		UNREFERENCED_PARAMETER(output);

		return S_OK;
	}

	RpcHookCode StopTheServer(RpcArchive& input, RpcArchive& output)
	{
		UNREFERENCED_PARAMETER(input);

		HRESULT hr = E_FAIL;

		CHK(output.SetNamedValue(L"result", pRpcServer2->Stop()));
end:
		return hr;
	}

	RpcHookCode CallProcessPendingRequestsOnceOnServer3(RpcArchive& input, RpcArchive& output)
	{
		UNREFERENCED_PARAMETER(input);
		UNREFERENCED_PARAMETER(output);

		HRESULT hr = pRpcServer3->ProcessPendingRequests();

		return hr;
	}

	RpcHookCode MultiInputMultiOutput(RpcArchive& input, RpcArchive& output)
	{
		HRESULT hr = E_FAIL;

		unsigned char inputParam1, inputParam2;
		double inputParam3;
		CHK(input.GetNamedValue(L"param01", &inputParam1));
		CHK(input.GetNamedValue(L"param02", &inputParam2));
		CHK(input.GetNamedValue(L"param03", &inputParam3));

		if(inputParam1 != 112 || inputParam2 != 79 || inputParam3 != 93.125)
		{
			hr = E_INVALIDARG;
			goto end;
		}

		CHK(output.SetNamedValue(L"addOneAndTwo", inputParam1 + inputParam2));
		CHK(output.SetNamedValue(L"paramThreeSquared", inputParam3 * inputParam3));
		CHK(output.SetNamedValue(L"subtractTwoFromOne", inputParam1 - inputParam2));
end:
		return hr;
	}

	RpcHookCode HookTimesOut(RpcArchive& input, RpcArchive& output)
	{
		UNREFERENCED_PARAMETER(input);
		UNREFERENCED_PARAMETER(output);

		Sleep(31000);

		return S_OK;
	}

	RpcHookCode HookWaitTenSeconds(RpcArchive& input, RpcArchive& output)
	{
		UNREFERENCED_PARAMETER(input);
		UNREFERENCED_PARAMETER(output);

		Sleep(10000);

		return S_OK;
	}

	RpcHookCode ReturnLargeNumberOfValues(RpcArchive& input, RpcArchive& output)
	{
		HRESULT hr = E_FAIL;

		int numOutputValues;
		CHK(input.GetNamedValue(L"numOutputValues", &numOutputValues));

		for(int i = 0; i < numOutputValues; ++i)
		{
			wchar_t buf[128];
			swprintf_s(buf, L"out%03d", i);

			CHK(output.SetNamedValue(buf, i));
		}

end:
		return hr;
	}

	RpcHookCode ReturnErrorCode(RpcArchive& input, RpcArchive& output)
	{
		UNREFERENCED_PARAMETER(input);
		UNREFERENCED_PARAMETER(output);

		return E_FAIL;
	}

	RpcHookCode SimultaneousHookCalls(RpcArchive& input, RpcArchive& output)
	{
		HRESULT hr = E_FAIL;

		unsigned __int16 param;

		CHK(input.GetNamedValue(L"param", &param));

		if(param != 52 && param != 71)
		{
			hr = E_INVALIDARG;
			goto end;
		}

		CHK(output.SetNamedValue(L"result", param * 100));

end:
		return hr;
	}

	RpcHookCode LargeNumberOfInputValues(RpcArchive& input, RpcArchive& output)
	{
		HRESULT hr = E_FAIL;

		int numParams;
		CHK(input.GetNamedValue(L"numParams", &numParams));

		int sum = 0;
		for(int i = 0; i < numParams; ++i)
		{
			wchar_t paramName[128];
			swprintf_s(paramName, L"in%03d", i);

			int currentValue;
			CHK(input.GetNamedValue(paramName, &currentValue));

			sum += currentValue;
		}


		CHK(output.SetNamedValue(L"result", sum));
end:
		return hr;
	}

	RpcHookCode RetrieveMissingRpcArchiveField(RpcArchive& input, RpcArchive& output)
	{
		UNREFERENCED_PARAMETER(output);

		double notUsed;

		HRESULT hr = input.GetNamedValue(L"nonExistentField", &notUsed);
		if (FAILED(hr))
		{
			return ARCHIVE_FIELD_NOT_FOUND;
		}

		return S_OK;
	}

	RpcHookCode StringBufferIsTooSmall(RpcArchive& input, RpcArchive& output)
	{
		UNREFERENCED_PARAMETER(output);

		DWORD stringLength = 0;
		input.MeasureNamedStringValue(L"input", &stringLength);

		wchar_t* buffer = new wchar_t[stringLength];
		HRESULT hr = input.GetNamedStringValue(L"input", buffer, stringLength - 2, nullptr);

		delete[] buffer;

		if (FAILED(hr))
		{
			return STRING_BUFFER_TOO_SMALL;
		}

		return S_OK;
	}

	RpcHookCode RpcArchiveValueIsNull(RpcArchive& input, RpcArchive& output)
	{
		UNREFERENCED_PARAMETER(input);
		UNREFERENCED_PARAMETER(output);

		return ARCHIVE_VALUE_IS_NULL;
	}

	RpcHookCode HookOrderIncrementNoOutput(RpcArchive& input, RpcArchive& output)
	{
		UNREFERENCED_PARAMETER(input);
		UNREFERENCED_PARAMETER(output);

		testHookOrderCounter = testHookOrderCounter + 1;

		return S_OK;
	}

	RpcHookCode GetHookOrderCounter(RpcArchive& input, RpcArchive& output)
	{
		UNREFERENCED_PARAMETER(input);

		HRESULT hr = E_FAIL;

		CHK(output.SetNamedValue(L"result", testHookOrderCounter));
end:
		return hr;
	}

	RpcHookCode AddArchiveToArchive(RpcArchive& input, RpcArchive& output)
	{
		HRESULT hr = E_FAIL;

		// incoming RpcArchive object
		RpcArchive inRpcArchive;

		// for Output RpcArchive object
		RpcArchive outRpcArchive;

		// Getting the input RpcArchive object
		CHK(input.GetNamedValue(L"param01", &inRpcArchive));

		// Calling existing Hook Method
		CHK(InputOutput(inRpcArchive, outRpcArchive));

		// Setting the output of Hook method
		CHK(output.SetNamedValue(L"resultLevel1", outRpcArchive));

end:
		return hr;
	}

	RpcHookCode AddArchiveToArchiveWithIntColl(RpcArchive& input, RpcArchive& output)
	{
		HRESULT hr = E_FAIL;

		// incoming RpcArchive object
		RpcArchive inRpcArchive;

		// Output RpcArchive object
		RpcArchive outRpcArchive;

		// Getting the input RpcArchive object
		CHK(input.GetNamedValue(L"param01", &inRpcArchive));

		// Calling existing Hook method
		CHK(InputOutputIntCollection(inRpcArchive, outRpcArchive));

		// Setting the output of Hook method
		CHK(output.SetNamedValue(L"resultLevel1", outRpcArchive));

end:
		return hr;
	}

	RpcHookCode AddArchiveToArchiveWithStringColl(RpcArchive& input, RpcArchive& output)
	{
		HRESULT hr = E_FAIL;

		// incoming RpcArchive object
		RpcArchive inRpcArchive1;

		// incoming RpcArchive object
		RpcArchive inRpcArchive2;

		// Output RpcArchive object
		RpcArchive outRpcArchive1;

		// Output RpcArchive object
		RpcArchive outRpcArchive2;

		// Getting the input RpcArchive object
		CHK(input.GetNamedValue(L"param01", &inRpcArchive1));

		// Getting the input RpcArchive object
		CHK(inRpcArchive1.GetNamedValue(L"param01", &inRpcArchive2));

		// Calling existing Hook method
		CHK(InputOutputStringCollection(inRpcArchive2, outRpcArchive1));

		// Setting the RpcArchive object to out RpcArchive object
		CHK(outRpcArchive2.SetNamedValue(L"resultLevel2", outRpcArchive1));

		// Setting the output of Hook method
		CHK(output.SetNamedValue(L"resultLevel1", outRpcArchive2));

end:
		return hr;
	}

	RpcHookCode AddArchiveToArchiveWithInt64Coll(RpcArchive& input, RpcArchive& output)
	{
		HRESULT hr = E_FAIL;

		// incoming RpcArchive object
		RpcArchive inRpcArchive;

		// for Output RpcArchive object
		RpcArchive outRpcArchive;

		// Getting the input RpcArchive object
		CHK(input.GetNamedValue(L"param01", &inRpcArchive));

		// Calling existing Hook method
		CHK(InputOutputInt64Collection(inRpcArchive, outRpcArchive));

		// Setting the output of Hook method
		CHK(output.SetNamedValue(L"resultLevel1", outRpcArchive));

end:
		return hr;
	}

	RpcHookCode InputOutputArchiveCollectionToArchive(RpcArchive& input, RpcArchive& output)
	{
		HRESULT hr = E_FAIL;

		const DWORD InOutArchivesCount = 4;

		DWORD bufferLength = 0;

		CHK(input.MeasureNamedCollection(L"param01", &bufferLength));
		
		// to validate the length of collection
		if (bufferLength == InOutArchivesCount)
		{
			RpcArchive inputParam01[InOutArchivesCount];
			RpcArchive outputParam01[InOutArchivesCount];

			CHK(input.GetNamedCollection(L"param01", inputParam01, bufferLength));

			__int32 inputParamInt = 0;
			CHK(inputParam01[0].GetNamedValue(L"param01", &inputParamInt));

			CHK(outputParam01[0].SetNamedValue(L"result", inputParamInt+1));

			__int64 inputParamLong = 0i64;
			CHK(inputParam01[1].GetNamedValue(L"param01", &inputParamLong));

			CHK(outputParam01[1].SetNamedValue(L"result", inputParamLong-1));

			float inputParamFloat = 0.0f;
			CHK(inputParam01[2].GetNamedValue(L"param01", &inputParamFloat));

			CHK(outputParam01[2].SetNamedValue(L"result", inputParamFloat*3));

			CHK(EchoStrings(inputParam01[3], outputParam01[3]));

			CHK(output.SetNamedCollection(L"resultLevel1", outputParam01, InOutArchivesCount));
		}
		else
		{
			hr = E_FAIL;
		}

end:
		return hr;
	}

	RpcHookCode InputOutputArchiveCollectionToArchiveCollections(RpcArchive& input, RpcArchive& output)
	{
		HRESULT hr = E_FAIL;

		const DWORD InOutArchivesCount = 4;

		DWORD bufferLength = 0;

		CHK(input.MeasureNamedCollection(L"param01", &bufferLength));

		// to validate the length of collection
		if (bufferLength == InOutArchivesCount)
		{
			RpcArchive inputParam01[InOutArchivesCount];
			RpcArchive outputParam01[InOutArchivesCount];
		
			CHK(input.GetNamedCollection(L"param01", inputParam01, bufferLength));

			// setting output
			CHK(InputOutputIntCollection(inputParam01[0], outputParam01[0]));
			CHK(InputOutputInt64Collection(inputParam01[1], outputParam01[1]));
			CHK(InputOutputFloatCollection(inputParam01[2], outputParam01[2]));
			CHK(InputOutputStringCollection(inputParam01[3], outputParam01[3]));

			CHK(output.SetNamedCollection(L"resultLevel1", outputParam01, InOutArchivesCount));
		}
		else
		{
			hr = E_FAIL;
		}

end:
		return hr;    
	}

	RpcHookCode InputRpcArchiveToAnotherRpcArchiveForLoad(RpcArchive& input, RpcArchive& output)
    {
		HRESULT hr = E_FAIL;

		RpcArchive rpcArchiveInput;
		RpcArchive rpcArchiveOutput;      
                        
		CHK(input.GetNamedValue(L"rpcMultilevel", &rpcArchiveInput));

		__int32 level = 0;
		CHK(input.GetNamedValue(L"level", &level));                                                                            

		if (level > 0)
		{
			for (int i=1; i<level; i++)
			{                    
				RpcArchive rpcArchiveInputNode;   
                                                                     
				// Getting the input RpcArchive object
				CHK(rpcArchiveInput.GetNamedValue(L"rpcNode", &rpcArchiveInputNode));                            
				rpcArchiveInput=rpcArchiveInputNode;
			}           
                       
			CHK(InputOutputMultipleCollectionsForRpcArchive(rpcArchiveInput,rpcArchiveOutput));

			for (int i=1; i<level; i++)
			{                              
				RpcArchive rpcArchiveOutputTemp;
				CHK(rpcArchiveOutputTemp.SetNamedValue(L"rpcNodeResult",rpcArchiveOutput));
				rpcArchiveOutput=rpcArchiveOutputTemp;                       
			}
                            
			CHK(output.SetNamedValue(L"rpcMultilevelResult", rpcArchiveOutput));
		}
		else
		{
			hr = E_FAIL;
		}
end:
        return hr;    
	}

    RpcHookCode InputRpcArchiveToAnotherRpcArchiveCollectionForLoad(RpcArchive& input, RpcArchive& output)
    {
        HRESULT hr = E_FAIL;

        RpcArchive rpcArchiveInput;
        RpcArchive rpcArchiveOutput;

        CHK(input.GetNamedValue(L"rpcMultilevel", &rpcArchiveInput));

        __int32 level = 0;
        CHK(input.GetNamedValue(L"level", &level));                   

		if (level > 0)
		{
			for (int i=1; i<level; i++)
			{                    
				RpcArchive rpcArchiveInputNode;   
                                                                     
				// Getting the input RpcArchive object
				CHK(rpcArchiveInput.GetNamedValue(L"rpcNode", &rpcArchiveInputNode));                            
				rpcArchiveInput=rpcArchiveInputNode;
			}    

			DWORD bufferLength = 0;

			CHK(rpcArchiveInput.MeasureNamedCollection(L"rpcCollection", &bufferLength));
                       
			if (bufferLength > 0)
			{
				std::unique_ptr<RpcArchive[]> inputParam01(new (std::nothrow) RpcArchive[bufferLength]);
				std::unique_ptr<RpcArchive[]> outputParam01(new (std::nothrow) RpcArchive[bufferLength]);

				if(inputParam01 != nullptr && outputParam01 != nullptr)
				{
					CHK(rpcArchiveInput.GetNamedCollection(L"rpcCollection", inputParam01.get(), bufferLength));
					for (DWORD i = 0; i < bufferLength; i++)
					{
						CHK(InputOutputMultipleCollectionsForRpcArchive(inputParam01[i], outputParam01[i])); 
					}
					rpcArchiveOutput.SetNamedCollection(L"rpcCollectionResult", outputParam01.get(), bufferLength);
            
					for (__int32 i = 1; i < level; i++)
					{                              
						RpcArchive rpcArchiveOutputTemp;
						CHK(rpcArchiveOutputTemp.SetNamedValue(L"rpcNodeResult",rpcArchiveOutput));
						rpcArchiveOutput=rpcArchiveOutputTemp;                       
					}
            
					CHK(output.SetNamedValue(L"rpcMultilevelResult", rpcArchiveOutput));
				}
				else
				{
					hr = E_OUTOFMEMORY;
				}
			}
			else
			{
				hr = E_FAIL;
			}
		}
		else
		{
			hr = E_FAIL;
		}
end:
		return hr;    
	}

    RpcHookCode InputOutputMultipleCollectionsForRpcArchive(RpcArchive& input, RpcArchive& output)
    {
        HRESULT hr = E_FAIL;

        // Part 1 - which is a string collection

        DWORD numStrings = 0;
        DWORD numStringCharacters = 0;

        CHK(input.MeasureNamedStringCollection(L"param01", &numStrings, &numStringCharacters));

		if (numStrings > 0)
		{
			std::unique_ptr<wchar_t*[]> stringBuffer1(new (std::nothrow) wchar_t*[numStrings]);
			std::unique_ptr<wchar_t[]> stringContents1(new (std::nothrow) wchar_t[numStringCharacters]);

			if (stringBuffer1 != nullptr && stringContents1 != nullptr)
			{
				input.GetNamedStringCollection(L"param01", stringBuffer1.get(), numStrings, stringContents1.get(), numStringCharacters);

				std::reverse(stringBuffer1.get(), stringBuffer1.get() + numStrings);

				CHK(output.SetNamedStringCollection(L"result1", stringBuffer1.get(), numStrings));
			}
			else
			{
				hr = E_OUTOFMEMORY;
				goto end;
			}
		}
		else
		{
			hr = E_FAIL;
			goto end;
		}

        // Part 2 - which is a float collection
        DWORD bufferLength = 0;

        CHK(input.MeasureNamedCollection(L"param02", &bufferLength));

		if (bufferLength > 0)
		{
			std::unique_ptr<float32[]> inputParam02(new (std::nothrow) float32[bufferLength]);

			if (inputParam02 != nullptr)
			{
				CHK(input.GetNamedCollection(L"param02", inputParam02.get(), bufferLength));

				std::reverse(inputParam02.get(), inputParam02.get() + bufferLength);

				CHK(output.SetNamedCollection(L"result2", inputParam02.get(), bufferLength));
			}
			else
			{
				hr = E_OUTOFMEMORY;
				goto end;
			}
		}
		else
		{
			hr = E_FAIL;
			goto end;
		}

        // Part 3- which is an int collection    
		bufferLength = 0;

        CHK(input.MeasureNamedCollection(L"param03", &bufferLength));
        
		if (bufferLength > 0)
		{
			std::unique_ptr<__int32[]> inputParam03(new (std::nothrow) __int32[bufferLength]);
		
			if (inputParam03 != nullptr)
			{
				CHK(input.GetNamedCollection(L"param03", inputParam03.get(), bufferLength));

				std::reverse(inputParam03.get(), inputParam03.get() + bufferLength);

				CHK(output.SetNamedCollection(L"result3", inputParam03.get(), bufferLength));
			}
			else
			{
				hr = E_OUTOFMEMORY;
				goto end;
			}
		}
		else
		{
			hr = E_FAIL;
		}
end:

		return hr;
	}

	RpcHookCode ThrowException(RpcArchive& input, RpcArchive& output)
    {
		HRESULT hr = E_FAIL;

		int errorCode = 0;

		CHK(input.GetNamedValue(L"errorCode", &errorCode));

		switch(errorCode)
		{
		case 600:
			hr = TEST_EXCEPTION_1;
			break;
		case 601:
			hr = TEST_EXCEPTION_2;
			break;
		case 602:
			hr = TEST_EXCEPTION_3;
			break;
		default:
			hr = S_OK;
			break;
		}

end:
		UNREFERENCED_PARAMETER(output);
		return hr;
	}

	RpcHookCode AddBinaryDataToArchive(RpcArchive& input, RpcArchive& output)
	{
		HRESULT hr = E_FAIL;

		// incoming RpcArchive object
		RpcBinaryData inRpcBinaryData;
		RpcBinaryData outRpcBinaryData;
		
		// Getting the input RpcArchive object
		CHK(input.GetNamedValue(L"param01", &inRpcBinaryData));		
		
		// for Output RpcArchive object
		outRpcBinaryData = inRpcBinaryData;

		std::reverse(outRpcBinaryData.begin(), outRpcBinaryData.end());

		// Setting the output of Hook method
		CHK(output.SetNamedValue(L"param01", outRpcBinaryData));

end:
		return hr;
	}

	
	RpcHookCode AddBinaryDataCollectionToArchive(RpcArchive& input, RpcArchive& output)
	{
		HRESULT hr = E_FAIL;

		DWORD bufferLength = 0;

		CHK(input.MeasureNamedCollection(L"param01", &bufferLength));

		if (bufferLength > 0)
		{
			std::unique_ptr<RpcBinaryData[]> inputParam01(new (std::nothrow) RpcBinaryData[bufferLength]);

			if(inputParam01 != nullptr)
			{
				CHK(input.GetNamedCollection(L"param01", inputParam01.get(), bufferLength));
		
				std::for_each(inputParam01.get(), inputParam01.get() + bufferLength, [](RpcBinaryData& elem)
				{
					std::reverse(elem.begin(), elem.end());
				});

				CHK(output.SetNamedCollection(L"result", inputParam01.get(), bufferLength ));
			}
			else
			{
				hr = E_OUTOFMEMORY;
			}
		}

end:
		return hr;
	}

	RpcHookCode NoInputBinaryDataOutput(RpcArchive& input, RpcArchive& output)
	{
		HRESULT hr = E_FAIL;
		
		UNREFERENCED_PARAMETER(input);
		const int arraySize = 5;

		std::shared_ptr<uint8> data(new uint8[arraySize], std::default_delete<uint8[]>());
		for(uint8 i = 0; i < arraySize; ++i)
		{
			data.get()[i] = i;
		}

		RpcBinaryData outRpcBinaryData(data, arraySize);
		
		// Getting the input RpcArchive object
		CHK(output.SetNamedValue(L"result", outRpcBinaryData));

end:
		return hr;
	}

	RpcHookCode EchoBinary(RpcArchive& input, RpcArchive& output)
	{
		HRESULT hr = E_FAIL;

		RpcBinaryData binaryData;

		CHK(input.GetNamedValue(L"blob", &binaryData));

		CHK(output.SetNamedValue(L"blob", binaryData));

	end:
		return hr;
	}

	RpcHookCode StartServerWithQueueSize(RpcArchive& input, RpcArchive& output)
	{
		UNREFERENCED_PARAMETER(output);

		// {456F8F5C-0B90-465B-8D4E-D10CC8D690C0}
		static const GUID guid4 = { 0x456f8f5c, 0xb90, 0x465b, { 0x8d, 0x4e, 0xd1, 0xc, 0xc8, 0xd6, 0x90, 0xc0 } };
		
		HRESULT hr = E_FAIL;

		unsigned int queueSize = 0;

		CHK(input.GetNamedValue(L"queueSize", &queueSize));

		if (pRpcServerWithQueueSize != nullptr)
		{
			delete pRpcServerWithQueueSize;
		}		

		pRpcServerWithQueueSize = new RpcServer(guid4, queueSize);

		CHK(pRpcServerWithQueueSize->Start());

	end:
		return hr;
	}

	RpcHookCode StopServerWithQueueSize(RpcArchive& input, RpcArchive& output)
	{
		UNREFERENCED_PARAMETER(input);
		UNREFERENCED_PARAMETER(output);

		if (pRpcServerWithQueueSize != nullptr)
		{
			delete pRpcServerWithQueueSize;
			pRpcServerWithQueueSize = nullptr;
		}

		return S_OK;
	}

	RpcHookCode RunServerWithQueueSize(RpcArchive& input, RpcArchive& output)
	{
		UNREFERENCED_PARAMETER(input);
		UNREFERENCED_PARAMETER(output);

		if (pRpcServerWithQueueSize != nullptr)
		{
			pRpcServerWithQueueSize->ProcessPendingRequests();
		}

		return S_OK;
	}

}}}}}}